Longest mountain in array [Two Pointers]

Time: O(N); Space: O(1); medium

Let’s call any (contiguous) subarray B (of A) a mountain if the following properties hold: B.length >= 3 There exists some 0 < i < B.length - 1 such that B[0] < B[1] < … B[i-1] < B[i] > B[i+1] > … > B[B.length - 1] (Note that B could be any subarray of A, including the entire array A.)

Given an array A of integers, return the length of the longest mountain. Return 0 if there is no mountain.

Example 1:

Input: A = [2, 1, 4, 7, 3, 2, 5]

Output: 5

Explanation:

  • The largest mountain is [1,4,7,3,2] which has length 5.

Example 2:

Input: A = [2, 2, 2]

Output: 0

Explanation:

  • There is no mountain.

Notes:

  • 0 <= A.length <= 10000

  • 0 <= A[i] <= 10000

Follow up:

Can you solve it using only one pass? Can you solve it in O(1) space?

Intuition Without loss of generality, a mountain can only start after the previous one ends. This is because if it starts before the peak, it will be smaller than a mountain starting previous; and it is impossible to start after the peak.

Algorithm For a starting index base, let’s calculate the length of the longest mountain A[base], A[base+1], …, A[end]. If such a mountain existed, the next possible mountain will start at base = end; if it didn’t, then either we reached the end, or we have A[base] > A[base+1] and we can start at base + 1.

Example

Here is a worked example on the array A = [1, 2, 3, 2, 1, 0, 2, 3, 1]: Worked example of A = [1,2,3,2,1,0,2,3,1]

base starts at 0, and end travels using the first while loop to end = 2 (A[end] = 3), the potential peak of this mountain. After, it travels to end = 5 (A[end] = 0) during the second while loop, and a candidate answer of 6 (base = 0, end = 5) is recorded.

Afterwards, base is set to 5 and the process starts over again, with end = 7 the peak of the mountain, and end = 8 the right boundary, and the candidate answer of 4 (base = 5, end = 8) being recorded.

[1]:
class Solution1(object):
    def longestMountain(self, A):
        """
        :type A: List[int]
        :rtype: int
        """
        N = len(A)
        result = base = 0

        while base < N:
            end = base
            if end + 1 < N and A[end] < A[end + 1]: #if base is a left-boundary
                #set end to the peak of this potential mountain
                while end+1 < N and A[end] < A[end+1]:
                    end += 1

                if end + 1 < N and A[end] > A[end + 1]: # if end is really a peak..
                    # set 'end' to right-boundary of mountain
                    while end + 1 < N and A[end] > A[end + 1]:
                        end += 1
                    # record candidate answer
                    result = max(result, end - base + 1)

            base = max(end, base + 1)

        return result
[2]:
s = Solution1()
A = [2, 1, 4, 7, 3, 2, 5]
assert s.longestMountain(A) ==  5
A = [2, 2, 2]
assert s.longestMountain(A) ==  0
[3]:
class Solution2(object):
    def longestMountain(self, A):
        """
        :type A: List[int]
        :rtype: int
        """
        result, up_len, down_len = 0, 0, 0

        for i in range(1, len(A)):
            if (down_len and A[i-1] < A[i]) or A[i-1] == A[i]:
                up_len, down_len = 0, 0
            up_len += A[i-1] < A[i]
            down_len += A[i-1] > A[i]
            if up_len and down_len:
                result = max(result, up_len + down_len + 1)

        return result
[4]:
s = Solution2()
A = [2, 1, 4, 7, 3, 2, 5]
assert s.longestMountain(A) ==  5
A = [2, 2, 2]
assert s.longestMountain(A) ==  0